﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once

#include "ICurve3d.h"
#include "GbmpConstants.h"

namespace gcmp
{
    /// \brief 三维NURBS曲线
    ///
    class INurbsCurve3d : public ICurve3d
    {
        DEFINE_CAST_DERIVED(gcmp::INurbsCurve3d, gcmp::ICurve3d);
    public:
        /// \brief 根据控制顶点和节点矢量数据来创建一般NURBS曲线
        /// \param degree 曲线次数
        /// \param controlPoints 控制顶点数组
        /// \param weights 各控制顶点的权值数组，长度应与控制顶点个数相同
        /// \param knotVector 节点矢量，长度应等于：次数+控制顶点个数+1
        /// \return 三维NURBS曲线
        GCMP_GEOMETRY_INTERFACE_EXPORT static OwnerPtr<INurbsCurve3d> Create(int degree, const std::vector<Vector3d>& controlPoints,
            const std::vector<double>& weights, const std::vector<double>& knotVector);

        /// \brief 根据控制顶点来创建一般NURBS曲线，自动计算节点矢量，并设置各控制顶点的权值为1
        /// \param degree 曲线次数
        /// \param controlPoints 控制顶点数组
        /// \return 三维NURBS曲线
        GCMP_GEOMETRY_INTERFACE_EXPORT static OwnerPtr<INurbsCurve3d> Create(int degree, const std::vector<Vector3d>& controlPoints);

        /// \brief 根据插值点来创建一般NURBS曲线
        /// \param throughPts 插值点数组
        /// \param tangentVts 插值点切向量数组（数组默认为空，此时仅考虑插值点，不为空时需要和插值点数组数目相同）
        /// \return 三维NURBS曲线
        GCMP_GEOMETRY_INTERFACE_EXPORT static OwnerPtr<INurbsCurve3d> Create(const std::vector<Vector3d>& throughPts, const std::vector<Vector3d>& tangentVts = {});

        /// \brief 拟合生成样条曲线, 由误差控制拟合次数和段数，算法内部根据输入点集points的弦长进行参数化
        /// \param[in] points 拟合点集
        /// \param[in] bKeepEndPoint 是否保持严格经过端点
        /// \param[in] dDistEpsilon 容差含义： 拟合容差；
        ///                         容差使用场景：对于拟合后得到三维Nurbs曲线上在插值点对应参数上的实际点与原始点points对比，两点之间距离如果小于拟合容差，曲线拟合成功；否则，拟合失败；
        ///                         容差建议dDistEpsilon = max(插值点最大模长, 曲线近似弦长 / 弧长)*相对容差
        ///                         其中相对容差不宜过小否则拟合结果容易产生局部震荡现象，相对容差建议取1e - 4以上
        /// \return 三维NURBS曲线
        GCMP_GEOMETRY_INTERFACE_EXPORT static OwnerPtr<INurbsCurve3d> CreateNurbsCurve3dFittingWithTol(const std::vector<Vector3d>& points, bool bKeepEndPoint, double dDistEpsilon);

        /// \brief 通过控制顶点构造指定次数的三维周期型样条曲线。
        /// 
        /// 注意：因为周期型曲线得到的控制点数不满足“节点矢量数=次数+控制点数+1”这一特征，所以通过周期型曲线得到的控制点和节点矢量数据来创建一般NURBS曲线时不能保证成功。
        /// \param degree 曲线次数
        /// \param controlPoints 控制顶点集合，用户保证控制点数（首末去重之后的数量）大于次数
        /// \return 三维周期型样条曲线
        GCMP_GEOMETRY_INTERFACE_EXPORT static OwnerPtr<INurbsCurve3d> CreatePeriodicNurbsCurve3d(int degree, std::vector<Vector3d>& controlPoints);

        /// \brief 通过插值点构造三次的三维周期型样条曲线。
        /// 
        /// 注意：因为周期型曲线得到的控制点数不满足“节点矢量数=次数+控制点数+1”这一特征，所以通过周期型曲线得到的控制点和节点矢量数据来创建一般NURBS曲线时不能保证成功。
        /// \param throughPoints 插值点集合,用户保证非重插值点数大于1
        /// \return 三维周期型样条曲线
        GCMP_GEOMETRY_INTERFACE_EXPORT static OwnerPtr<INurbsCurve3d> CreatePeriodicNurbsCurve3d(const std::vector<Vector3d>& throughPoints);


    public:
        /// \brief 获得NURBS曲线次数
        /// \return NURBS曲线次数
        virtual int GetDegree() const = 0;

        /// \brief 获得NURBS曲线控制顶点
        /// \param controlPoints NURBS曲线控制顶点数组
        /// \return 无
        virtual void GetControlPoints(std::vector<Vector3d>& controlPoints) const = 0;

        /// \brief 获得NURBS曲线节点数组
        /// \param knots NURBS曲线节点数组
        /// \return 无
        virtual void GetKnots(std::vector<double> &knots) const = 0;

        /// \brief 获得NURBS曲线控制顶点的权值数组
        /// \param weights NURBS曲线控制顶点的权值数组
        /// \return 无
        virtual void GetWeights(std::vector<double> &weights) const = 0;

        /// \brief 延伸NURBS曲线
        /// \param length 延伸长度
        /// \param isTail true = 正向延伸，false = 反向延伸
        /// \return 是否延伸成功：true = 是；false = 否
        /// \see 当输入的length为负数时，函数放弃操作并返回false
        virtual bool Extend(double length, bool isTail) = 0;

        /// \brief     按切向延长曲线
        /// \param[in] length   延伸的曲线长度,需要大于Constants::DOUBLE_EPS
        /// \param[in] isTail      true 为正向延伸， false 为反向延伸
        /// \return 是否延伸成功：true = 是；false = 否
        virtual bool ExtendByTangent(double length, bool isTail) = 0;

        /// \brief 判断样条曲线是否周期型。
        /// 
        /// 注意：此方法与ICurve::IsPeriodic()不同。
        /// ICurve::IsPeriodic()是判断一般曲线是否周期型，只要求闭合以及range可以按闭合周期延续即可。
        /// 而此处INurbsCurve3d::IsPeriodicForNurbsCurve3d()是针对样条曲线进行判断是否周期型。
        /// 周期型样条曲线除了ICurve::IsPeriodic()满足周期型的条件外，还需要控制点和节点矢量的节点值也能按周期延续，属于特殊情况特殊判断。
        /// \return 如果是周期型则返回true；否则返回false。
        virtual bool IsPeriodicNurbsCurve3d() const = 0;

        /// \brief     合并两条nurbs曲线，合并后的曲线为当前曲线，曲线合并后连续性不好，用于其他几何处理风险大，不推荐使用
        /// \param[in] rightCurve 另一条nurbs曲线 
        /// \return    true 成功 false 失败
        virtual bool MergeCurve(INurbsCurve3d *  rightCurve) = 0;

        /// \brief     合并两条G1连续的 NURBS 曲线，合并后的曲线为当前曲线
        /// \param[in] rightCurve   另一条 NURBS 曲线
        /// \param[in] dEpsilon 判断能否达到 G1 连续的容差，判断曲线连接处切向是否相等的容差；默认容差为0.0001
        /// \return    true 成功 false 失败
        virtual bool MergeCurveG1(INurbsCurve3d* rightCurve, double dEpsilon = Constants::DOUBLE_EPS) = 0;

        /// \brief     对当前Nurbs曲线升阶，返回新的 NURBS 曲线
        /// \return    新的 NURBS 曲线
        virtual OwnerPtr<INurbsCurve3d> DegreeElevation() = 0;

        /// \brief     对当前Nurbs曲线升阶 本地操作
        /// \return    void
        virtual void DegreeElevationSelf() = 0;

    public:
        virtual ~INurbsCurve3d() {}
    };
}


